package lanczos.core;

import java.util.ArrayList;

import uml.graph.Aresta;
import uml.graph.Grafo;
import uml.graph.Vertice;
import cern.colt.matrix.impl.SparseDoubleMatrix2D;

public class ClusteringApp {

	public String fidlervectors = "";
	
	public String getClusters(Grafo g){
		String message = "";
		Grafo grafo = g;
		@SuppressWarnings("unused")
		ArrayList<Aresta> arestasOutside = grafo.getArestas();
		ArrayList<Aresta> arestasInside = grafo.getArestas();
		LanczosDecomposition lanczos = null;
		int x = 1;
		
		

		ArrayList<Vertice> verticesOutside = new ArrayList<Vertice>();
		ArrayList<Vertice> verticesInside = new ArrayList<Vertice>();
				
		do {
			
			verticesOutside = new ArrayList<Vertice>();
			verticesInside = new ArrayList<Vertice>();
			
			double[][] lapMatrix = grafo.getMatrizLaplace();

			int nPositives = 0;
			
			String cluster = "";
			String posibleCluster = "";
			String positives = "";
			String negatives = "";
			
			ArrayList<Vertice> verticesPositivos = new ArrayList<Vertice>();
			ArrayList<Vertice> verticesNegativos = new ArrayList<Vertice>();
			
			do {
				
				cluster = "";
				positives = "";
				negatives = "";
				nPositives = 0;
				
				verticesPositivos = new ArrayList<Vertice>();
				verticesNegativos = new ArrayList<Vertice>();
				
				lanczos = identifyClustering(lapMatrix);
				
				for(int i=0; i < lanczos.getEigenvectors().rows(); i++){
					if(lanczos.getEigenvectors().get(i, 1) >= 0){
						positives += grafo.getVertice(i).getNome() + "  ";
						nPositives++;
						verticesPositivos.add( grafo.getVertice(i) );
					} else {
						negatives += grafo.getVertice(i).getNome() + "  ";
						verticesNegativos.add( grafo.getVertice(i) );
					}
				}
				
				if(nPositives == 0 || nPositives == grafo.getQtdVertices()){
					System.out.println("Can't get the fidler vector... trying again...");
				}
				
			} while (nPositives == 0 || nPositives == grafo.getQtdVertices());
			
			System.out.println("nPositives="+nPositives);
			System.out.println("(lanczos.getEigenvectors().rows()-nPositives)="+(lanczos.getEigenvectors().rows()-nPositives));
			System.out.println("positives="+positives);
			System.out.println("negatives="+negatives);
			
			fidlervectors +="Fidler Vector "+x+" = [";
			System.out.print("Fidler Vector (i,1) = [");
			for(int i=0; i < lanczos.getEigenvectors().rows(); i++){
				fidlervectors +=lanczos.getEigenvectors().get(i, 1) + ", ";
				System.out.print(lanczos.getEigenvectors().get(i, 1) + ", ");
			}
			fidlervectors += "]\n";
			System.out.println("]");
			
			System.out.print("Fidler Vector (1,i) = [");
			for(int i=0; i < lanczos.getEigenvectors().columns(); i++)
				System.out.print(lanczos.getEigenvectors().get(1, i) + ", ");
			System.out.println("]");
			
			if(nPositives < (lanczos.getEigenvectors().rows()-nPositives)){
				cluster = positives;
				posibleCluster = negatives;
				verticesInside.addAll(verticesPositivos);
				verticesOutside.addAll(verticesNegativos);
			} else {
				cluster = negatives;
				posibleCluster = positives;
				verticesInside.addAll(verticesNegativos);
				verticesOutside.addAll(verticesPositivos);
			}
			
			arestasInside = getEdges(grafo, verticesInside, verticesInside);
			arestasOutside = getEdges(grafo, verticesOutside, verticesInside);

			grafo = new Grafo(verticesOutside, getEdges(grafo, verticesOutside, verticesOutside));
			
			System.out.println("arestasInside.size()="+arestasInside.size());
			System.out.println("grafo.getQtdArestas()="+grafo.getQtdArestas());
			if(verticesInside.size()>1 && new Grafo(verticesInside, arestasInside).isConnected()){
				if(!(arestasInside.size() < grafo.getQtdArestas() && arestasInside.size() > 0 && grafo.getQtdArestas() > 0)){
					if(grafo.isConnected()){
						message += "\n- Componente " + x++ + ": ";
						message += cluster;
						
						message += "\n- Componente " + x++ + ": ";
						message += posibleCluster;
					} else {
						message += "\n- Componente " + x++ + ": ";
						message += positives + negatives;
						
					}
				} else {
					message += "\n- Componente " + x++ + ": ";
					message += cluster;
				}
			} else {
				message += "\n- Componente " + x++ + ": ";
				message += positives + negatives;
				break;
			}
			
		} while (arestasInside.size() < grafo.getQtdArestas() && arestasInside.size() > 0 && grafo.getQtdArestas() > 0);
		
		return message;
	}

	private ArrayList<Aresta> getEdges(Grafo grafo, ArrayList<Vertice> vertices, ArrayList<Vertice> vertices2){
		ArrayList<Aresta> arestas = new ArrayList<Aresta>();

		for(int j=0; j < vertices.size(); j++)
			for(int k=0; k < vertices2.size(); k++){
				Aresta aresta = grafo.getArestaEntreVertices(vertices.get(j), vertices2.get(k));
				if(aresta != null && !arestas.contains(aresta)){
					arestas.add( aresta );
				}
			}
		return arestas;
	}

	private LanczosDecomposition identifyClustering(double[][] lapMatrix){
		LanczosDecomposition decomp = null;

		if(lapMatrix[0] != null){
			int size = lapMatrix[0].length;

			SparseDoubleMatrix2D matrix = new SparseDoubleMatrix2D(size,size);

			for(int i=0; i < size; i++)
				for(int j=0; j < size; j++)
					matrix.set(i, j, lapMatrix[i][j]);

			decomp = new LanczosDecomposition(matrix, false);
		}
		
		return decomp;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		/**
		 * Calculating the 1st iteration
		 */

		//Building the Laplacian Matrix
		SparseDoubleMatrix2D matrix = new SparseDoubleMatrix2D(10,10);

		matrix.set(0, 0, 2); matrix.set(0, 1, 0); matrix.set(0, 2, -2); matrix.set(0, 3, 0); matrix.set(0, 4, 0); matrix.set(0, 5, 0); matrix.set(0, 6, 0); matrix.set(0, 7, 0); matrix.set(0, 8, 0); matrix.set(0, 9, 0);
		matrix.set(1, 0, 0); matrix.set(1, 1, 5); matrix.set(1, 2, -5); matrix.set(1, 3, 0); matrix.set(1, 4, 0); matrix.set(1, 5, 0); matrix.set(1, 6, 0); matrix.set(1, 7, 0); matrix.set(1, 8, 0); matrix.set(1, 9, 0);
		matrix.set(2, 0, -2); matrix.set(2, 1, -5); matrix.set(2, 2, 14); matrix.set(2, 3, -5); matrix.set(2, 4, 0); matrix.set(2, 5, 0); matrix.set(2, 6, -2); matrix.set(2, 7, 0); matrix.set(2, 8, 0); matrix.set(2, 9, 0);
		matrix.set(3, 0, 0); matrix.set(3, 1, 0); matrix.set(3, 2, -5); matrix.set(3, 3, 5); matrix.set(3, 4, 0); matrix.set(3, 5, 0); matrix.set(3, 6, 0); matrix.set(3, 7, 0); matrix.set(3, 8, 0); matrix.set(3, 9, 0);
		matrix.set(4, 0, 0); matrix.set(4, 1, 0); matrix.set(4, 2, 0); matrix.set(4, 3, 0); matrix.set(4, 4, 4); matrix.set(4, 5, -2); matrix.set(4, 6, -2); matrix.set(4, 7, 0); matrix.set(4, 8, 0); matrix.set(4, 9, 0);
		matrix.set(5, 0, 0); matrix.set(5, 1, 0); matrix.set(5, 2, 0); matrix.set(5, 3, 0); matrix.set(5, 4, -2); matrix.set(5, 5, 4); matrix.set(5, 6, -2); matrix.set(5, 7, 0); matrix.set(5, 8, 0); matrix.set(5, 9, 0);
		matrix.set(6, 0, 0); matrix.set(6, 1, 0); matrix.set(6, 2, -2); matrix.set(6, 3, 0); matrix.set(6, 4, -2); matrix.set(6, 5, -2); matrix.set(6, 6, 9); matrix.set(6, 7, -3); matrix.set(6, 8, 0); matrix.set(6, 9, 0);
		matrix.set(7, 0, 0); matrix.set(7, 1, 0); matrix.set(7, 2, 0); matrix.set(7, 3, 0); matrix.set(7, 4, 0); matrix.set(7, 5, 0); matrix.set(7, 6, -3); matrix.set(7, 7, 10); matrix.set(7, 8, -3); matrix.set(7, 9, -4);
		matrix.set(8, 0, 0); matrix.set(8, 1, 0); matrix.set(8, 2, 0); matrix.set(8, 3, 0); matrix.set(8, 4, 0); matrix.set(8, 5, 0); matrix.set(8, 6, 0); matrix.set(8, 7, -3); matrix.set(8, 8, 3); matrix.set(8, 9, 0);
		matrix.set(9, 0, 0); matrix.set(9, 1, 0); matrix.set(9, 2, 0); matrix.set(9, 3, 0); matrix.set(9, 4, 0); matrix.set(9, 5, 0); matrix.set(9, 6, 0); matrix.set(9, 7, -4); matrix.set(9, 8, 0); matrix.set(9, 9, 4);

		//Running the Lanczos decomposition
		LanczosDecomposition decomp = new LanczosDecomposition(matrix);

		//Printing results
		for(int i=0; i < decomp.getEigenvectors().columns(); i++)
			System.out.println("Value " + (i+1) + ": " + decomp.getEigenvectors().get(i, 1));
		System.out.println("Eigenvalue: " + decomp.getEigenvalues().get(1));


		/**----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------**/

		/**
		 * Calculating the 2nd iteration
		 */

		//Building the Laplacian Matrix
		SparseDoubleMatrix2D matrix2 = new SparseDoubleMatrix2D(6,6);

		matrix2.set(0, 0, 4); matrix2.set(0, 1, -2); matrix2.set(0, 2, -2); matrix2.set(0, 3, 0); matrix2.set(0, 4, 0); matrix2.set(0, 5, 0);
		matrix2.set(1, 0, -2); matrix2.set(1, 1, 4); matrix2.set(1, 2, -2); matrix2.set(1, 3, 0); matrix2.set(1, 4, 0); matrix2.set(1, 5, 0);
		matrix2.set(2, 0, -2); matrix2.set(2, 1, -2); matrix2.set(2, 2, 7); matrix2.set(2, 3, -3); matrix2.set(2, 4, 0); matrix2.set(2, 5, 0);
		matrix2.set(3, 0, 0); matrix2.set(3, 1, 0); matrix2.set(3, 2, -3); matrix2.set(3, 3, 10); matrix2.set(3, 4,-3); matrix2.set(3, 5, -4);
		matrix2.set(4, 0, 0); matrix2.set(4, 1, 0); matrix2.set(4, 2, 0); matrix2.set(4, 3, -3); matrix2.set(4, 4, 3); matrix2.set(4, 5, 0);
		matrix2.set(5, 0, 0); matrix2.set(5, 1, 0); matrix2.set(5, 2, 0); matrix2.set(5, 3, -4); matrix2.set(5, 4, 0); matrix2.set(5, 5, 4);

		//Running the Lanczos decomposition
		LanczosDecomposition decomp2 = new LanczosDecomposition(matrix2);

		//Printing results
		for(int i=0; i < decomp2.getEigenvectors().columns(); i++)
			System.out.println("Value " + (i+1) + ": " + decomp2.getEigenvectors().get(i, 1));
		System.out.println("Eigenvalue: " + decomp2.getEigenvalues().get(1));



	}

}
